﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Grishko.SerilizeTools;
using System.Reflection;
using System.Xml;
using Grishko.DataTier.MetaInfo;
using System.IO;
using System.Web;
using System.Data.SqlClient;
using System.Threading;
using System.Data;

namespace Grishko.DataTier
{
   /// <summary>
   /// Базовый класс бизнес объектов.
    /// Явно поодерживает IEntity,ISerializableItem 
    /// Неявно IStoredObject.    
   /// </summary>
 
   public  class EntitySingle:Entity
   {
       
        
       #region Saver
       /// <summary>
       /// Интсрумент - ворирует имя файла для entity хранящихся в файлах
       /// </summary>
       /// <param name="obj"></param>
       /// <returns></returns>
       protected  static string GetFileName(EntitySingle obj)
       {
           return obj.TagName + obj.GetType ().ToString ()+".xml";
       }

       protected class Saver
       {
           public static Saver GetSaver(Entity obj)
           {
               if (obj.ImSql)
                   return new SQLSaver() { obj = obj };
               
               if(obj.ImFile)
                   return new FileSaver() { obj = obj };

               return new Saver ();
           }
           protected Entity obj { get; set; }
           virtual public void Save (){}
           /// <summary>
           /// асинхронное сохранение объекта
           /// </summary>
           virtual public void AsynSave() 
           {
               Save();
           }
       }
       /// <summary>
       /// Cохраняльщик в БД
       /// </summary>
       protected class SQLSaver : Saver
       {    
        override public void Save()
             {
                 if (EntityState.Loaded == obj.State)
                     return;
               
                   SecuretyContext SC = AppContext.GetSC();
                   ISqlSignature o = (ISqlSignature)obj ;
                   SqlTools tls = new SqlTools(SC);
                   string sp = obj.State.Equals(EntityState.Deleted) ? o.DeleteProcedure : o.SaveProcedure;
                   tls.ExecuteCommand_NonQuery(sp, (obj as EntitySingle ));             
               
             }
        public override void AsynSave()
        {
            if (EntityState.Loaded == obj.State)
                return;

            SecuretyContext SC = new SecuretyContext("");
            SC = AppContext.GetSC();
            ISqlSignature o = (ISqlSignature)obj;
            SqlTools tls = new SqlTools(SC);
            string sp = obj.State.Equals(EntityState.Deleted) ? o.DeleteProcedure : o.SaveProcedure;
            SqlCommand  cmd = tls.GetSaveCommand(sp, (obj as EntitySingle));

            Thread t = new Thread(SqlTools.ExecuteCommand_NonQuery);
            t.Start(cmd);
        }

    }
       protected class FileSaver : Saver
       {           
           public override void Save()
           {
               StreamWriter w =null;
               try
               {
                IFileSignature I = (IFileSignature)obj;
                string s = HttpContext.Current.Request.PhysicalApplicationPath + "\\" + I.Directory;
                Directory.CreateDirectory(s);
                w= File.CreateText(s + "\\" + I.FileName);
                w.Write(obj.ToXml (false));
               }
               finally 
               { 
                   w.Close();
               }
           }
           
       }
       #endregion

       #region Loader
       protected class Loader
       {
           public static Loader  GetLoader(Entity obj)
           {
               if (obj.ImSql)
                   return new SQLLoader() { obj = obj };

               if (obj.ImFile)
                   return new FileLoader () { obj = obj };

               return new Loader ();
           }
           protected Entity obj { get; set; }
           public virtual void Load(){}

       }

       protected class SQLLoader:Loader 
       {
           public override void Load()
           {
               SecuretyContext SC = AppContext.GetSC();
               try
               {
                   ISqlSignature o = (ISqlSignature)obj;
                   SqlTools tls = new SqlTools(SC);
                   XmlReader r = tls.ReadXml(o.LoadProcedure, obj);
                   XmlDocument d = new XmlDocument();
                   d.Load(r);
                   obj.ReadXml(d.FirstChild);
               }
               catch 
               {
                   throw;
               }
               finally
               {
                   if (SC.Connection.State == System.Data.ConnectionState.Open) SC.Connection.Close();
               }
               
           }
       }
       protected class FileLoader : Loader
       {
           public override void Load()
           {
               StreamReader r = null;
               try
               {
                   IFileSignature I = (IFileSignature)obj;
                   string s = HttpContext.Current.Request.PhysicalApplicationPath + "\\" + I.Directory;
                   r = File.OpenText(s + "\\" + I.FileName);
                   string sXml = r.ReadToEnd();
                   obj.ReadXml(sXml);
               }
               catch 
               {
                 throw ;
               }
               
               finally
               {
                   if (null != r)
                       r.Close();
               }
           }
       }

       #endregion

       private string _ID = Guid.NewGuid().ToString();
       [SrchItemInfo(TxtPropertyName = SrchItemInfo.EmptyString )]
       public virtual string ID { get { return _ID; } set { _ID = value; } }

       #region Write/Save
       /// <summary>
       /// (IEntity)
       /// </summary>
       internal  override void WriteXml(System.Xml.XmlWriter writer, string ObjectName, bool Full)
       {
           try
           {
               //Определяем тип объекта 
               Type MyType = this.GetType();
               // начало узла
               writer.WriteStartElement(ObjectName);
               // Обязательно пишем тип и сборку 
               writer.WriteAttributeString(SerilizeContext.TypePref, MyType.ToString());
               writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());

               /// Пишем базовые не объектные свойства
               string sValue = string.Empty ;
               foreach (PropertyInfo inf in AttPropertiesList)
               {
                   object v = inf.GetValue(this, null);
                   if (null == v) continue;
                   sValue = v.ToString();
                   if (String.IsNullOrEmpty(v.ToString())) continue;
                   writer.WriteAttributeString(inf.Name, sValue);
               }

               //пишем перечисления
               foreach (PropertyInfo inf in EnumPropertiesList)
               {
                   object v = inf.GetValue(this, null);
                   if (null == v) continue;
                   if (String.IsNullOrEmpty(v.ToString())) continue;
                   writer.WriteAttributeString(inf.Name, v.ToString());
               }

               // пишем объекты
               foreach (PropertyInfo inf in EntityPropertiesList)
               {
                   object v = inf.GetValue(this, null);
                   if (null == v) continue;

                   Entity I = (Entity)v;
                   if (Full)
                   {
                       I.WriteXml(writer, inf.Name, Full);
                       continue;
                   }

                   //// вариант для Full- false
                   if (v is ISqlSignature)
                       I.WriteShortXml(writer, inf.Name);
                   else
                       I.WriteXml(writer, inf.Name, Full);

                   continue;
               }

               writer.WriteEndElement();
           }
           catch (Exception e)
           {
               string s = e.Message;
               throw;
           }
       }


       #region IStoredObject

       /// <summary>
       /// (IStoredObject)
       /// </summary>
       /// <param name="sParams"></param>
       /// <returns></returns>
       internal  override string GetXPatch(string[] sParams)
       {           
           string XPatch = string.Format("{0}[@ID=''{1}'']", sParams);
           return XPatch;
       }

       /// <summary>
       /// (IStoredObject)
       /// </summary>
       internal override SrchInfo GetSrchInfo(string RootID)
       {
            SrchInfo res = new SrchInfo();
            string[] p = { TagName, this.ID };
            this.PopulateSrchItemes(res, this, RootID, GetXPatch(p));
            return res;
       }

       /// <summary>
       /// (IStoredObject)
       /// Прописать vStorage vSrchInfo
       /// </summary>
       internal override void PopulateSrchItemes(SrchInfo res, Entity obj, string RootId, string XPatch)
       {
           ////Пишем в хранилище vStorage информацию о объекте 
           if(! SrchInfo.IsNotSrchStorageLinksObject(obj.GetType ())) // Нет атрибута не писать - объект в хранилище?
           {
               //Если это  SQL объеткт и он есть корень 
               if (obj.ImSql && (obj as EntitySingle).ID == RootId)
               {
                   SrchStorageLinksItem LinkInfo = SrchInfo.GetSrchStorageLinksItem((EntitySingle)obj, RootId, XPatch);
                   res.SrchStorageLinks.Add(LinkInfo);
               }

               //Или если это - дочерний несамомтоятельный объект 
               if (!obj.ImSql)
               {
                   SrchStorageLinksItem LinkInfo = SrchInfo.GetSrchStorageLinksItem((EntitySingle)obj, RootId, XPatch);
                   res.SrchStorageLinks.Add(LinkInfo);
               }
           }



           // Просматриваем AttPropertiesList - на предмет - атрибутов поиска 
           foreach (PropertyInfo inf in obj.AttPropertiesList)
           {
               //Свойство не содержит атрибуты базы поиска?
               if (!inf.IsDefined(typeof(SrchItemInfo), true))
                   continue;

               //Получаем экземпляр 
               object v = inf.GetValue(obj, null);

               //Это поле пишется в базу поиска
               object[] arr = inf.GetCustomAttributes(typeof(SrchItemInfo), true);
               SrchItemInfo srchItemInfo = (SrchItemInfo)arr[0];
               
               //если  поле - sql функция - формируем тело функции
               if (srchItemInfo.ValueIsSqlFunction)
               {
                   v = srchItemInfo.GetText_SqlFunctionCall (obj);
               }
 

               // Прописываем объекты в харнилище                    
               SrchObjectPropertyStorageItem vp = SrchInfo.GetSrch_AttProperty((obj as EntitySingle).TagName,
                                                                                srchItemInfo,
                                                                                inf,
                                                                                v, 
                                                                                (obj as EntitySingle).ID);
               if (null != vp)
                    res.SrchObjectPropertyStorage.Add(vp);

           }

           //Объектные свойства 
           foreach (PropertyInfo inf in obj.EntityPropertiesList)
           {

               //Получаем экземпляр 
               object PropVal = inf.GetValue(obj, null);
               
               //- Объект null?  - ничего о нем не пишем
               if (null == PropVal)
                   continue;
               
               // ЗНАЧЕНИЕ ДЕРЕВЯННОГО СПРАВОЧНИКА
               if (PropVal is ITreeDictionaryItemValue)
                   if (inf.IsDefined(typeof(SrchItemInfo), true))
                   {
                       SrchItemInfo SrchItemInfo = SrchInfo.GetSrchInfo(inf);
                       // Прописываем как TreeValue как атрибут в харнилище                    
                       SrchObjectPropertyStorageItem vp = SrchInfo.GetSrch_AttProperty((obj as EntitySingle).TagName,
                                                                                        SrchItemInfo,
                                                                                        inf,
                                                                                        (obj as EntitySingle).ID+"."+((EntitySingle)PropVal).ID,
                                                                                        (obj as EntitySingle).ID);
                       if (null != vp)
                           res.SrchObjectPropertyStorage.Add(vp);

                       EntityList l = SrchInfo.GetSrchTreeEntityItem(SrchItemInfo,
                                                                            inf, 
                                                                            (PropVal as TreeValueProperty) , 
                                                                            this);
                       
                       foreach (EntitySingle item in l)
                       {
                           res.SrchObjectPropertyTreeStorage.Add(item);
                       }
                       continue;

                   }
                   else
                       continue;





               // ЗНАЧЕНИЕ - КОЛЛЕКЦИЯ
               if (PropVal is EntityList)
               {
                   // Если коллекция пишется в базу поиска 
                   if (inf.IsDefined(typeof(SrchItemInfo), true))
                   {
                       foreach (EntitySingle item in (PropVal as EntityList))
                       {
                           object[] arr = inf.GetCustomAttributes(typeof(SrchItemInfo), true);
                           SrchItemInfo srch_atr = (SrchItemInfo)arr[0];
                           res.SrchObjectPropertyStorage.Add(SrchInfo.GetSrchEntityCollectionItem(srch_atr, inf.Name, item.GetType(), item, this));
                       }
                   }
                       

                    // Прописываем объекты в харнилище                    
                        (PropVal as Entity).PopulateSrchItemes(res, (PropVal as Entity), RootId, XPatch + "/" + inf.Name + "/");

                   continue;
               }
               
               //формируем pXPatch 
               string[] pXPatch = { inf.Name, ((EntitySingle)PropVal).ID };               

               //Свойство не содержит атрибуты базы поиска?
               if (!inf.IsDefined(typeof(SrchItemInfo), true))
               {
                   // Рекурсивно пишем                         
                    (PropVal as Entity).PopulateSrchItemes(res, (PropVal as Entity), RootId, XPatch + "/" + ((Entity)obj).GetXPatch(pXPatch));                
                   continue;
               }

               // Перед рекурсией - пишем о существовании этого свойства
               object[] arr1 = inf.GetCustomAttributes(typeof(SrchItemInfo), true);
               SrchItemInfo srch_atr1 = (SrchItemInfo)arr1[0];
               res.SrchObjectPropertyStorage.Add(SrchInfo.GetSrchEntityItem(srch_atr1, inf, PropVal, obj));


               // Поисков. инф. для объектов Isql - не пишем - сами напишут
               if (PropVal is ISqlSignature)
                   continue;
               
               // Рекурсивно пишем                     
               (PropVal as Entity).PopulateSrchItemes(res, (PropVal as Entity), RootId, XPatch + "/" + ((Entity)obj).GetXPatch(pXPatch));

           }
       }

       /// <summary>
       /// Поисковая информация. (IStoredObject)
       /// </summary>
       [EntityNonSerialized]
       public override SrchInfo SrchInfoVal
       {
           get
           {               
               SrchInfo inf = GetSrchInfo(ID);        
               return inf;
           }

       }

       #endregion

       /// <summary>
       /// (IEntity)
       /// </summary>
       internal  override void WriteShortXml(System.Xml.XmlWriter writer, string ObjectName)
       {            
            Type MyType = this.GetType();
            PropertyInfo[] MyFields = MyType.GetProperties();

            // начало узла
            writer.WriteStartElement(ObjectName);
            //// Обязательно пишем тип и сборку 
            writer.WriteAttributeString(SerilizeContext .TypePref, MyType.ToString());
            writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());
            writer.WriteAttributeString(SerilizeContext.TagNamePref, this.TagName);
            writer.WriteAttributeString("ID", this.ID);
            writer.WriteEndElement();
        
       }

       /// <summary>
       /// insert, update - асинхронно 
       /// delete синхронно. Сохраняется только текущий объект - для дочерних объектов поддерживающих ISqlSignature - save не вызывается. 
       /// </summary>
       public override void Save()
       {
           if (this.State == EntityState.Deleted)
               this.Update();
           else
               Saver.GetSaver(this).AsynSave();

       }

       /// <summary>
       /// Синхронное сохранение
       /// </summary>
       public override void Update()
       {
           Saver.GetSaver(this).Save();

       }


       public override void UpdateWitchChild()
       {
           Saver.GetSaver(this).Save();

           //Save - нижележащим узлам 
           foreach (PropertyInfo inf in EntityPropertiesList)
           {

               object v = inf.GetValue(this, null);
               if (null == v) continue;

               ((IEntity)v).UpdateWitchChild();

           }

       }
       /// <summary>       
       /// В отличие от save обесепчивает эскадацию метода Save - по всем дочерним объектам 
       /// </summary>
       public override  void SaveWitchChild()
       {
           Saver.GetSaver(this).Save();

           //Save - нижележащим узлам 
           foreach (PropertyInfo inf in EntityPropertiesList)
           {

               object v = inf.GetValue(this, null);
               if (null == v) continue;

               ((IEntity)v).SaveWitchChild();

           }

       }


    
       #endregion

       #region Read/Load
       /// <summary>
       /// (IEntity)
       /// </summary>
       public override void Load()
       {          
           Loader.GetLoader(this).Load();           
           //прогрузка объектных свойств
           PropertyLoad();
       }
       /// <summary>
       /// Прогружает  свойства IEntity
       /// </summary>
       public override void PropertyLoad()
       {
           foreach (PropertyInfo inf in EntityPropertiesList)
           {
               object v = inf.GetValue(this, null);
               if (null == v) continue;
               ((IEntity)v).Load();
           }
       }

       #endregion

   }
}
